package com.terry.proxy.socket;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.terry.proxy.application.cache.DBCache;
import com.terry.proxy.application.cache.DBServerCache;
import com.terry.proxy.util.TcpConsumer;
import com.terry.proxy.util.SocketCreateUtil;
import com.terry.proxy.application.entity.ProxyClient;
import com.terry.proxy.application.entity.ProxyClientTunnel;
import com.terry.proxy.common.Msg;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * socket 代理服务端
 *
 * @author terry
 * @version 1.0
 * @date 2022/12/14 14:35
 */
@Slf4j
public abstract class ISocketProxyServer {

    /**
     * 四种角色：
     * 服务端代理、服务端真实
     * 客户端代理、客户端真实
     */

    // key 浏览器访客id
    public static Map<String, Channel> realVisitorChannel = new HashMap<>();

    // 客户端id
    public static Map<String, Channel> clientProxyChannel = new ConcurrentHashMap<>();

    @Resource
    public DBServerCache dbServerCacheImpl;

    // 更新的定时器
    public static final ScheduledExecutorService updateExecutor = Executors.newSingleThreadScheduledExecutor();

    /**
     * 初始化服务端（1.初始化系统配置、2.初始化数据库、3.开启服务端真实 监听端口、4.开启服务端代理 监听端口）
     */
    @EventListener(ApplicationReadyEvent.class)
    public void initServer() {
        dbServerCacheImpl.initSysConfig();
        log.info("初始化端口绑定...");
        dbServerCacheImpl.addClientAll();
        updateExecutor.scheduleAtFixedRate(() -> {
            dbServerCacheImpl.checkClientConnect();
            // 注册监听端口
        }, 0, 30, TimeUnit.SECONDS);
        this.openServer();
    }

    /**
     * 开启服务端
     */
    public abstract void openServer();

    /**
     * 代理连接
     */
    public boolean proxyConnect(Msg msg, Channel channel) {
        String clientId = msg.getHeader().getClientId();
        // Channel channel = clientProxyChannel.get(clientId);
        // 验证客户端
        String errorMsg = dbServerCacheImpl.verifyClientError(clientId, msg);
        if (StrUtil.isNotEmpty(errorMsg)) {
            sendMsg(Msg.PROXY_CONNECT_ERROR, msg.getHeader(), errorMsg.getBytes(StandardCharsets.UTF_8), channel);
            return false;
        }
        dbServerCacheImpl.updateConnect(clientId, DBServerCache.Y);
        return true;
    }

    /**
     * 代理响应
     */
    public void proxyResponse(Msg msg) {
        Msg.Header header = msg.getHeader();
        Channel channel = realVisitorChannel.get(header.getVisitorId());
        if (channel != null) {
            ByteBuf buf = channel.alloc().buffer(msg.getData().length);
            buf.writeBytes(msg.getData());
            channel.writeAndFlush(buf);
        }
    }

    /**
     * 处理真实服务连接（用户访问地址 -> 真实服务，用于生成访客id、发送给客户端代理 创建客户端访客、延迟读消息）
     */
    public void handleRealConnect(ChannelHandlerContext ctx, String clientId, Integer tunnelId) {
        ProxyClient proxyClient = DBServerCache.client.get(clientId);
        Channel channel = ctx.channel();
        if (proxyClient.getIsConnect().equals(DBServerCache.N)) {
            log.error("错误的请求 客户端 id{}, tunnelId {},无连接",clientId,tunnelId);
            channel.close();
            return;
        }
        // 访客
        String visitorId = IdUtil.simpleUUID();
        realVisitorChannel.put(visitorId, channel);
        channel.attr(SocketCreateUtil.VID).set(visitorId);
        channel.attr(SocketCreateUtil.CID).set(clientId);
        channel.attr(SocketCreateUtil.TID).set(tunnelId);
    }

    @Value("${proxy.clientId:1}")
    private String sendClientId;

    /**
     * 处理真实服务请求（用户访问地址 -> 真实服务，用于发送给访客客户端数据）
     */
    public void handleRealMessage(ChannelHandlerContext ctx, ByteBuf buf) {
        byte[] bytes = new byte[buf.readableBytes()];
        buf.readBytes(bytes);
        // 代理服务器发送数据到客户端了
        String visitorId = ctx.channel().attr(SocketCreateUtil.VID).get();
        String clientId = ctx.channel().attr(SocketCreateUtil.CID).get();
        Integer tunnelId = ctx.channel().attr(SocketCreateUtil.TID).get();
        // Msg.sendMsg(PROXY_REQUEST, new Header(clientId, tunnelId, visitorId), bytes, clientProxyChannel.get(clientId));
        ProxyClientTunnel proxyClientTunnel = DBServerCache.getclientTunnel(tunnelId);
        sendMsg(Msg.PROXY_REQUEST, new Msg.Header(clientId, tunnelId, visitorId, null, sendClientId, proxyClientTunnel), bytes, clientProxyChannel.get(clientId));
    }

    /**
     * 真实服务关闭事件（服务端访客服务 -> 客户端访客服务，用于释放资源）
     */
    public void handleRealClose(ChannelHandlerContext ctx) {
        log.info("关闭连接");
        Channel channel = ctx.channel();
        String visitorId = channel.attr(SocketCreateUtil.VID).get();
        if (visitorId == null) {
            return;
        }
        String clientId = channel.attr(SocketCreateUtil.CID).get();
        Integer tunnelId = channel.attr(SocketCreateUtil.TID).get();
        sendMsg(Msg.VISITOR_SERVER_CLOSE, new Msg.Header(clientId, tunnelId, visitorId, null, null, null), "关闭".getBytes(StandardCharsets.UTF_8), clientProxyChannel.get(clientId));
        closeChannel(visitorId);
    }

    public void sendMsg(String type, Msg.Header header, byte[] bytes, Channel channel){
        Msg.sendMsg(type, header, bytes, channel);
    }

    public void closeChannel(String vid){
        SocketCreateUtil.closeChannel(realVisitorChannel, vid);
    }
}
